#This file is part of The Tweetographer.

#The Tweetographer is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.

#The Tweetographer is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with The Tweetographer.  If not, see <http://www.gnu.org/licenses/>.

import re, MySQLdb, datetime
class parser:
 def __init__(self):
     self.address = "((\d{1,4}-)?\d{1,4} (\w+)(\s)((Ave)|(Rd)|(Blvd)|(Avenue)|(St)|(Street)|(Highway)|(Hwy)|(Boulevard)|(Plaza)|(Square)|(Sq)|(Way))+)"
     self.dayName = "((?P<identifier>(this|next) )?(?P<day>Sunday|Monday|Tuesday|Weds|Wednesday|Thursday|Friday|Saturday)+)"
     self.monthName = "((January|February|March|April|May|June|July|August|October|September|November|December)+)"
     self.timeExp = "((1+[012]+)|([123456789]{1}))(((:|\s)[0-5]+[0-9]+))?(\s)?((a|A|p|P)(m|M))"
     self.dateExp = "((?P<month>\d{1,2})/(?P<day>\d{1,2})(/(?P<year>\d{4}|\d{2}))?)"
     self.daysExp = "((?P<dayFirst>\d{1,2})?(\s)?(?P<monthFirst>January|February|March|April|May|June|July|August|October|September|November|December)(\s)?(?P<year>\d{4})|(?P<month>January|February|March|April|May|June|July|August|October|September|November|December)(\s)?((?P<day>([0-9]?[0-9]))(?P<tail>st|nd|rd|th)?))"     
     self.special = "(\s?(?P<special>today|tonight|tomorrow)+)"
     self.foursqExp = "http://4sq.com/.*$"
     self.act = "((going to)|(headed to))"     
     self.exp = [re.compile(x, re.I) for x in [self.address,self.dayName,self.act,self.monthName,self.timeExp,self.special,self.foursqExp,self.daysExp,self.dateExp]]
     self.confidentTweets = []
     self.junkTweets = []
     self.foursqTweets = []
     self.upTweet = MySQLConnect()
     self.scores = {'address': 30, 'date': 65, 'dayName': 20, 'time': 30, 'day': 60, 'month': 20, 'special': 30, 'act': 10}


 def monthToNumber(self,month):
     month = month.lower()
     if month == 'january':
         return 1
     elif month == 'february':
         return 2
     elif month == 'march':
         return 3
     elif month == 'april':
         return 4
     elif month == 'may':
         return 5
     elif month == 'june':
         return 6
     elif month == 'july':
         return 7
     elif month == 'august':
         return 8
     elif month == 'september':
         return 9
     elif month == 'october':
         return 10
     elif month == 'november':
         return 11
     elif month == 'december':
         return 12
 def getYear(self,m):
     try:
         year = int(m.group('year'))
     except:
         year = 2011
     if year < 100:
         year += 2000
     return year
    
 def isGood(self,tweet):
        for e in range(len(self.exp)):
                m = self.exp[e].search(tweet.text)
                if m:
                        if e == 0:
                                tweet.hasAddress = True
                        if e == 1:
                                tweet.hasDayName = True
                        if e == 2:
                                tweet.hasAct = True
                        if e == 3:
                                tweet.hasMonth = True
                        if e == 4:
                                tweet.hasTime = True
                        if e == 5:
                                tweet.hasSpecial = True
                                if m.group('special') == "today" or m.group('special') == "tonight":
                                        tweet.eventDate = datetime.date.today()
                                else:
                                        tweet.eventDate = datetime.date.today()
                                        tweet.eventDate = tweet.eventDate.replace(day=tweet.eventDate.day+1)
                        if e == 6:
                                tweet.hasFoursq = True
                        if e == 7:
                                tweet.hasDay = True
                                if m.group('monthFirst') and m.group('dayFirst'):
                                        year = self.getYear(m)
                                        if year < 2011 or year > 2012:
                                                tweet.hasDay = False
                                        if (int(m.group('dayFirst')) < 1) or (int(m.group('dayFirst')) > 31):
                                                tweet.hasDay = False
                                        if tweet.hasDay == True:
                                                tweet.eventDate = datetime.date(2011,int(self.monthToNumber(m.group('monthFirst'))),int(m.group('dayFirst')))
                                elif m.group('monthFirst'):
                                        year = self.getYear(m)
                                        if year < 2011 or year > 2012:
                                                tweet.hasDay = False
                                        else:
                                                tweet.eventDate = datetime.date(2011,int(self.monthToNumber(m.group('monthFirst'))),1)
                                else:
                                        if (int(m.group('day')) < 1) or (int(m.group('day')) > 31):
                                                tweet.hasDay = False
                                        else:
                                                tweet.eventDate = datetime.date(2011,int(self.monthToNumber(m.group('month'))),int(m.group('day')))
                        if e == 8:
                                month = int(m.group('month'))
                                if  (month > 0 and month < 13) and (abs(datetime.date.today().month - month) < 2): #if valid month and event is relevant (within 2 months)
                                        tweet.hasDate = True
                                        year = self.getYear(m)
                                        if year < 2011 or year > 2012 or (int(m.group('day')) < 1) or (int(m.group('day')) > 31):
                                                tweet.hasDate = False
                                        else:
                                                tweet.eventDate = datetime.date(year,int(m.group('month')),int(m.group('day')))                 
        tweet.updateMatches()
        for test in tweet.regexMatches:
                if test == True:
                        return tweet
        return False

 def translate(self,tweet):
     tweetTest = self.isGood(tweet)
     if tweetTest:
         self.weightTweets(tweetTest) #for mysql

 def scoreTweet(self,tweet):
     if tweet.hasAddress:
         tweet.score += self.scores['address']
     if tweet.hasDayName:
         tweet.score += self.scores['dayName']
     if tweet.hasAct:
         tweet.score += self.scores['act']
     if tweet.hasMonth:
         tweet.score += self.scores['month']
     if tweet.hasTime:
         tweet.score += self.scores['time']         
     if tweet.hasSpecial:
         tweet.score += self.scores['special']
     if tweet.hasDay:
         tweet.score += self.scores['day']
     if tweet.hasDate:
         tweet.score += self.scores['date']
     #bonus rounds!
     if tweet.hasDayName and (tweet.hasMonth or tweet.hasTime):
         tweet.score += 150
     if tweet.hasMonth == True and tweet.hasDay:
         tweet.score += 100
     if tweet.hasTime and tweet.hasSpecial:
         tweet.score += 100
     return tweet
             
 def weightTweets(self, tweet): #tweet 'weighting' function. examines criteria that tweets pass and makes assumptions of confidence that an event exists.
    #since the program will be running indefinitely, in order to to cut down on memory overhead no tweets are stored in memory, rather are inserted into the database on the fly
    tweet = self.scoreTweet(tweet)
    if tweet.hasFoursq == True:
        pass
    if tweet.score >= 60:
        print "CONFIDENT TWEET:",[tweet.text,tweet.regexMatches], tweet.eventDate        
        try: #try to insert the event into the database
            self.upTweet.insertEvent(tweet)
            if len(self.confidentTweets) > 0: #when mysql is able to be checked again, if there are tweets in the reserve list, they are uploaded
                for savedTweet in self.confidentTweets:
                    self.upTweet.insertEvent(savedTweet)                    
                self.confidentTweets = [] #empty tweet list after upload
        except UnicodeEncodeError: #unicode presents some problems inserting into MySQL
            try:
                tweet.text = tweet.text.encode('utf-8') #unicode text must be re-encoded to utf-8 to work
                self.upTweet.insertEvent(tweet)
            except UnicodeEncodeError:
                    print "Unicode error, cannot insert"
        except MySQLdb.Error,e:
            print 'MySQL error',e
            self.confidentTweets.append(tweet) #in the case that mysql is offline when trying to insert, tweets are added to a reserve list until mysql can come back up."""
    else:
        #self.junkTweets.append([tweet.text,tweet.regexMatches])
        print "JUNK TWEET:",[tweet.text,tweet.regexMatches], tweet.eventDate

def findValuable(parser,tweetobjs):
    for tweet in tweetobjs:
        try:
            parser.translate(tweet)
        except:
            pass 
    parser.weightTweets(parser.keep)

def findValuable(parser,tweet):
    try:
        parser.translate(tweet)
    except:
        pass

class tweetObject:
    def __init__(self):
        #boolean variables for flagging confidence criteria
        self.hasDayName = False
        self.hasAddress = False
        self.hasDate = False
        self.hasTime = False
        self.hasDay = False
        self.hasAct = False
        self.hasMonth = False
        self.hasSpecial = False
        self.hasFoursq = False
        #tweet information, i.e. username, long/lat coordinates, tweet text
        self.longitude = None
        self.latitude = None
        self.userName = None
        self.city = 0
        self.text = ""
        self.score = 0
        self.eventDate = None
        #list comprehension of boolean variables for confidence criteria defined above
        self.regexMatches = [self.hasAddress,self.hasDayName,self.hasAct,self.hasMonth,self.hasTime,self.hasSpecial,self.hasFoursq,self.hasDay,self.hasDate]

    def updateMatches(self):#have to refresh the values for booleans when they are changed because the array is pass-by-value
        self.regexMatches = [self.hasAddress,self.hasDayName,self.hasAct,self.hasMonth,self.hasTime,self.hasSpecial,self.hasFoursq,self.hasDay,self.hasDate]

class MySQLConnect: #MySQL connection class
    def __init__(self):
        self.cursor = None #cursor object used to execute commands in MySQL
        self.maxID = 0 #maximum TweetID from table

    def connect(self): #connect method to establish connection to db
        try:
            conn = MySQLdb.connect (host = "tweetographer.db.7281171.hostedresource.com",
                                    user = "tweetographer",
                                    passwd = "CS2012Tweeter",
                                    db = "tweetographer")
        except MySQLdb.Error, e:
            raise
        self.cursor = conn.cursor()
    def GetCities(self):
        self.connect()
        self.cursor.execute("""SELECT * FROM Locations""")
        result_set = self.cursor.fetchall()
        return {(city,state): {'box': box,'id':id} for (id,city,state,box) in result_set}

    def updateCounter(self,count):
        try:
            self.connect()
            self.cursor.execute("""UPDATE counter SET count = count+%s""",count)
        except MySQLdb.Error:
            raise
        
    def insertEvent(self,tweet):
        self.connect()
        if tweet.eventDate == None:
            date = datetime.date.today().isoformat()
        else:
            date = tweet.eventDate.isoformat()
        if tweet.longitude == None:
            self.cursor.execute("""INSERT INTO Tweets (Username,Text,Confidence,EventDate,Location) VALUES (%s,%s,%s,%s,%s)""",(tweet.userName,str(tweet.text),tweet.score,date,tweet.city))
        else:
            self.cursor.execute("""INSERT INTO Tweets (Username, Text, Longitude, Latitude, Confidence,EventDate,Location) VALUES (%s,%s,%s,%s,%s,%s,%s)""",(tweet.userName,str(tweet.text),tweet.longitude,tweet.latitude,tweet.score,date,tweet.city))
